<template>
  <canvas
    ref="canvas"
    willReadFrequency
    @mousedown="mouseDown"
    @mousemove="mouseMove"
    @mouseup="mouseUp"
    @touchstart="touchStart"
    @touchmove="touchMove"
    @touchend="touchEnd"></canvas>
</template>

<script>
  export default {
    props: {
      width: {
        type: Number,
        default: 800,
      },
      height: {
        type: Number,
        default: 300,
      },
      lineWidth: {
        type: Number,
        default: 4,
      },
      lineColor: {
        type: String,
        default: '#000000',
      },
      bgColor: {
        type: String,
        default: '',
      },
      isCrop: {
        type: Boolean,
        default: false,
      },
      isClearBgColor: {
        type: Boolean,
        default: true,
      },
      format: {
        type: String,
        default: 'image/png',
      },
      quality: {
        type: Number,
        default: 1,
      },
    },
    data() {
      return {
        hasDrew: false,
        resultImg: '',
        points: [],
        canvasTxt: null,
        startX: 0,
        startY: 0,
        isDrawing: false,
        sratio: 1,
      };
    },
    computed: {
      ratio() {
        return this.height / this.width;
      },
      stageInfo() {
        return this.$refs.canvas.getBoundingClientRect();
      },
      myBg() {
        return this.bgColor ? this.bgColor : 'rgba(255, 255, 255, 0)';
      },
    },
    watch: {
      myBg: function (newVal) {
        this.$refs.canvas.style.background = newVal;
      },
    },
    beforeMount() {
      window.addEventListener('resize', this.$_resizeHandler);
    },
    beforeDestroy() {
      window.removeEventListener('resize', this.$_resizeHandler);
    },
    mounted() {
      const canvas = this.$refs.canvas;
      canvas.height = this.height;
      canvas.width = this.width;
      canvas.style.background = this.myBg;
      this.$_resizeHandler();
      // 在画板以外松开鼠标后冻结画笔
      document.onmouseup = () => {
        this.isDrawing = false;
      };
    },
    methods: {
      $_resizeHandler() {
        const canvas = this.$refs.canvas;
        if (!canvas) return;
        canvas.style.width = this.width + 'px';
        const realw = parseFloat(window.getComputedStyle(canvas).width);
        canvas.style.height = this.ratio * realw + 'px';
        this.canvasTxt = canvas.getContext('2d');
        this.canvasTxt.scale(1 * this.sratio, 1 * this.sratio);
        this.sratio = realw / this.width;
        this.canvasTxt.scale(1 / this.sratio, 1 / this.sratio);
      },
      // pc
      mouseDown(e) {
        e = e || event;
        e.preventDefault();
        this.isDrawing = true;
        this.hasDrew = true;
        let obj = {
          x: e.offsetX,
          y: e.offsetY,
        };
        this.drawStart(obj);
      },
      mouseMove(e) {
        e = e || event;
        e.preventDefault();
        if (this.isDrawing) {
          let obj = {
            x: e.offsetX,
            y: e.offsetY,
          };
          this.drawMove(obj);
        }
      },
      mouseUp(e) {
        e = e || event;
        e.preventDefault();
        let obj = {
          x: e.offsetX,
          y: e.offsetY,
        };
        this.drawEnd(obj);
        this.isDrawing = false;
      },
      // mobile
      touchStart(e) {
        e = e || event;
        e.preventDefault();
        this.hasDrew = true;
        if (e.touches.length === 1) {
          let obj = {
            x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
            y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top,
          };
          this.drawStart(obj);
        }
      },
      touchMove(e) {
        e = e || event;
        e.preventDefault();
        if (e.touches.length === 1) {
          let obj = {
            x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
            y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top,
          };
          this.drawMove(obj);
        }
      },
      touchEnd(e) {
        e = e || event;
        e.preventDefault();
        if (e.touches.length === 1) {
          let obj = {
            x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
            y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top,
          };
          this.drawEnd(obj);
        }
      },
      // 绘制
      drawStart(obj) {
        this.startX = obj.x;
        this.startY = obj.y;
        this.canvasTxt.beginPath();
        this.canvasTxt.moveTo(this.startX, this.startY);
        this.canvasTxt.lineTo(obj.x, obj.y);
        this.canvasTxt.lineCap = 'round';
        this.canvasTxt.lineJoin = 'round';
        this.canvasTxt.lineWidth = this.lineWidth * this.sratio;
        this.canvasTxt.stroke();
        this.canvasTxt.closePath();
        this.points.push(obj);
      },
      drawMove(obj) {
        this.canvasTxt.beginPath();
        this.canvasTxt.moveTo(this.startX, this.startY);
        this.canvasTxt.lineTo(obj.x, obj.y);
        this.canvasTxt.strokeStyle = this.lineColor;
        this.canvasTxt.lineWidth = this.lineWidth * this.sratio;
        this.canvasTxt.lineCap = 'round';
        this.canvasTxt.lineJoin = 'round';
        this.canvasTxt.stroke();
        this.canvasTxt.closePath();
        this.startY = obj.y;
        this.startX = obj.x;
        this.points.push(obj);
      },
      drawEnd(obj) {
        this.canvasTxt.beginPath();
        this.canvasTxt.moveTo(this.startX, this.startY);
        this.canvasTxt.lineTo(obj.x, obj.y);
        this.canvasTxt.lineCap = 'round';
        this.canvasTxt.lineJoin = 'round';
        this.canvasTxt.stroke();
        this.canvasTxt.closePath();
        this.points.push(obj);
        this.points.push({ x: -1, y: -1 });
      },
      // 操作
      generate(options) {
        let imgFormat = options && options.format ? options.format : this.format;
        let imgQuality = options && options.quality ? options.quality : this.quality;
        const pm = new Promise((resolve, reject) => {
          if (!this.hasDrew) {
            reject(`Warning: Not Signned!`);
            return;
          }
          var resImgData = this.canvasTxt.getImageData(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
          this.canvasTxt.globalCompositeOperation = 'destination-over';
          this.canvasTxt.fillStyle = this.myBg;
          this.canvasTxt.fillRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
          this.resultImg = this.$refs.canvas.toDataURL(imgFormat, imgQuality);
          var resultImg = this.resultImg;
          this.canvasTxt.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
          this.canvasTxt.putImageData(resImgData, 0, 0);
          this.canvasTxt.globalCompositeOperation = 'source-over';
          if (this.isCrop) {
            const crop_area = this.getCropArea(resImgData.data);
            var crop_canvas = document.createElement('canvas');
            const crop_ctx = crop_canvas.getContext('2d');
            crop_canvas.width = crop_area[2] - crop_area[0];
            crop_canvas.height = crop_area[3] - crop_area[1];
            const crop_imgData = this.canvasTxt.getImageData(...crop_area);
            crop_ctx.globalCompositeOperation = 'destination-over';
            crop_ctx.putImageData(crop_imgData, 0, 0);
            crop_ctx.fillStyle = this.myBg;
            crop_ctx.fillRect(0, 0, crop_canvas.width, crop_canvas.height);
            resultImg = crop_canvas.toDataURL(imgFormat, imgQuality);
            crop_canvas = null;
          }
          resolve(resultImg);
        });
        return pm;
      },
      reset() {
        this.canvasTxt.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height);
        if (this.isClearBgColor) {
          this.$emit('update:bgColor', '');
          this.$refs.canvas.style.background = 'rgba(255, 255, 255, 0)';
        }
        this.points = [];
        this.hasDrew = false;
        this.resultImg = '';
      },
      getCropArea(imgData) {
        var topX = this.$refs.canvas.width;
        var btmX = 0;
        var topY = this.$refs.canvas.height;
        var btnY = 0;
        for (var i = 0; i < this.$refs.canvas.width; i++) {
          for (var j = 0; j < this.$refs.canvas.height; j++) {
            var pos = (i + this.$refs.canvas.width * j) * 4;
            if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
              btnY = Math.max(j, btnY);
              btmX = Math.max(i, btmX);
              topY = Math.min(j, topY);
              topX = Math.min(i, topX);
            }
          }
        }
        topX++;
        btmX++;
        topY++;
        btnY++;
        const data = [topX, topY, btmX, btnY];
        return data;
      },
    },
  };
</script>

<style scoped>
  canvas {
    max-width: 100%;
    display: block;
  }
</style>
